Add PromptGuard guardrail integration#24268
Conversation
Add PromptGuard as a first-class guardrail vendor in LiteLLM's proxy, supporting prompt injection detection, PII redaction, topic filtering, entity blocklists, and hallucination detection via PromptGuard's /api/v1/guard API endpoint. Backend: - Add PROMPTGUARD to SupportedGuardrailIntegrations enum - Implement PromptGuardGuardrail (CustomGuardrail subclass) with apply_guardrail handling allow/block/redact decisions - Add Pydantic config model with api_key, api_base, ui_friendly_name - Auto-discovered via guardrail_hooks/promptguard/__init__.py registries Frontend: - Add PromptGuard partner card to Guardrail Garden with eval scores - Add preset configuration for quick setup - Add logo to guardrailLogoMap Tests: - 30 unit tests covering configuration, allow/block/redact actions, request payload construction, error handling, config model, and registry wiring
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@greptileai review |
Greptile SummaryThis PR adds PromptGuard as a new first-class guardrail vendor in LiteLLM, following the established pattern used by the ~25 other guardrail integrations in the codebase. The implementation covers the full stack: Python Previously flagged issues (HTTPX client allocation before credential validation, Key changes:
Confidence Score: 4/5
|
| Filename | Overview |
|---|---|
| litellm/proxy/guardrails/guardrail_hooks/promptguard/promptguard.py | Core guardrail hook implementing allow/block/redact decision handling, credential validation, HTTP request construction, and fail-open/fail-closed error handling. Previously flagged issues (resource allocation order, Python 3.8 typing, timeout, JSON error handling, redact path mutation) have all been addressed. |
| litellm/proxy/guardrails/guardrail_hooks/promptguard/init.py | Registry wiring for the new guardrail — exposes guardrail_initializer_registry and guardrail_class_registry using the SupportedGuardrailIntegrations.PROMPTGUARD enum value. Consistent with all other hook modules in the same directory. |
| litellm/types/proxy/guardrails/guardrail_hooks/promptguard.py | Pydantic config model with api_key, api_base, and block_on_error fields, correctly included in LitellmParams via multiple inheritance. ui_friendly_name() returns "PromptGuard" which feeds the logo lookup chain in the UI. |
| litellm/types/guardrails.py | Adds PROMPTGUARD = "promptguard" to SupportedGuardrailIntegrations enum and imports PromptGuardConfigModel into LitellmParams. Change is minimal and follows existing pattern. |
| tests/test_litellm/proxy/guardrails/guardrail_hooks/test_promptguard.py | 40 unit tests across 8 classes. All HTTP calls are mocked with patch.object. Tests cover configuration, allow/block/redact decisions, error handling (fail-open/fail-closed), payload construction, image passthrough, and registry wiring. |
| ui/litellm-dashboard/src/components/guardrails/guardrail_garden_configs.ts | Adds a promptguard preset with provider: "Promptguard". This matches the key generated by populateGuardrailProviderMap for a snake_case key of "promptguard" (capitalizes first word → "Promptguard"). |
| ui/litellm-dashboard/src/components/guardrails/guardrail_info_helpers.tsx | Adds PromptGuard key to guardrailLogoMap pointing to the new SVG. The lookup chain (guardrail_provider_map → DynamicGuardrailProviders → guardrailLogoMap) resolves correctly because ui_friendly_name() returns "PromptGuard" which matches the unquoted object key. |
Sequence Diagram
sequenceDiagram
participant Client
participant LiteLLMProxy as LiteLLM Proxy
participant PG as PromptGuardGuardrail
participant PGAPI as PromptGuard API
participant LLM as LLM Provider
Client->>LiteLLMProxy: POST /v1/chat/completions
LiteLLMProxy->>PG: apply_guardrail(inputs, "request")
PG->>PG: Build payload (messages, direction="input", model?, images?)
PG->>PGAPI: POST /api/v1/guard (X-API-Key, timeout=10s)
alt API error
PGAPI-->>PG: Exception
alt block_on_error=True
PG-->>LiteLLMProxy: raise exception
LiteLLMProxy-->>Client: 500 error
else block_on_error=False
PG-->>LiteLLMProxy: return inputs unchanged
end
else decision = "allow"
PGAPI-->>PG: {"decision": "allow"}
PG-->>LiteLLMProxy: return inputs unchanged
LiteLLMProxy->>LLM: Forward request
LLM-->>LiteLLMProxy: LLM response
LiteLLMProxy->>PG: apply_guardrail(inputs, "response")
PG->>PGAPI: POST /api/v1/guard (direction="output")
PGAPI-->>PG: {"decision": "allow"}
PG-->>LiteLLMProxy: return inputs unchanged
LiteLLMProxy-->>Client: 200 response
else decision = "block"
PGAPI-->>PG: {"decision": "block", "threat_type": "...", "confidence": ...}
PG-->>LiteLLMProxy: raise GuardrailRaisedException
LiteLLMProxy-->>Client: 400 Blocked by PromptGuard
else decision = "redact"
PGAPI-->>PG: {"decision": "redact", "redacted_messages": [...]}
PG->>PG: Update structured_messages and/or texts in inputs
PG-->>LiteLLMProxy: return modified inputs
LiteLLMProxy->>LLM: Forward redacted request
LLM-->>LiteLLMProxy: LLM response
LiteLLMProxy-->>Client: 200 response
end
Last reviewed commit: "Fix CI lint: black f..."
litellm/proxy/guardrails/guardrail_hooks/promptguard/promptguard.py
Outdated
Show resolved
Hide resolved
- P1: Update structured_messages (not just texts) when PromptGuard returns a redact decision, so PII redaction is effective for the primary LLM message path - P2: Validate credentials before allocating the HTTPX client so resources aren't acquired if PromptGuardMissingCredentials is raised - Add tests for structured_messages redaction and texts-only redaction
litellm/proxy/guardrails/guardrail_hooks/promptguard/promptguard.py
Outdated
Show resolved
Hide resolved
litellm/proxy/guardrails/guardrail_hooks/promptguard/promptguard.py
Outdated
Show resolved
Hide resolved
litellm/proxy/guardrails/guardrail_hooks/promptguard/promptguard.py
Outdated
Show resolved
Hide resolved
- Add block_on_error config (default fail-closed, configurable fail-open) - Declare supported_event_hooks (pre_call, post_call) like other vendors - Forward images from GenericGuardrailAPIInputs to PromptGuard API - Wrap API call in try/except for resilient error handling - Add comprehensive documentation page with config examples - Register docs page in sidebar alongside other guardrail providers - Expand test suite from 32 to 40 tests covering new functionality
ui/litellm-dashboard/src/components/guardrails/guardrail_info_helpers.tsx
Show resolved
Hide resolved
- Add explicit 10s timeout to async_handler.post() to prevent indefinite hangs when PromptGuard API is unresponsive - Guard redact path: only update inputs["texts"] when the key was originally present, avoiding phantom key injection - Add test: redact with structured_messages only does not create texts key (41 tests total)
|
All @greptileai review comments have been addressed: @ishaan-jaff - would appreciate your review when you get a chance. This adds PromptGuard as a first-class guardrail provider (backend hook, UI garden card, docs page, 41 tests). Happy to address any further feedback. |
…arams - Reformat promptguard.py to match CI black version (parenthesization) - Add PromptGuardConfigModel as base class of LitellmParams for proper Pydantic schema validation, consistent with all other guardrail vendors - Use litellm_params.block_on_error directly (now a typed field)
Closes #24272
Summary
Add PromptGuard as a first-class guardrail vendor in LiteLLM's proxy, appearing alongside existing partners in the Guardrail Garden UI.
PromptGuard is an AI security gateway that provides:
What's included
Backend (Python):
PROMPTGUARDadded toSupportedGuardrailIntegrationsenumPromptGuardGuardrail—CustomGuardrailsubclass implementingapply_guardrailviaPOST /api/v1/guarddecision: "allow"→ pass through unchangeddecision: "block"→ raiseGuardrailRaisedExceptionwith threat detailsdecision: "redact"→ return modified inputs with redacted content (updates bothtextsandstructured_messages)block_on_error(fail-closed by default, fail-open optional)supported_event_hooksdeclaration (pre_call,post_call)GenericGuardrailAPIInputs.imagesapi_key,api_base,block_on_error,ui_friendly_name()guardrail_hooks/promptguard/__init__.pyregistries (zero manual wiring)Frontend (TypeScript):
guardrailLogoMapDocumentation:
docs/proxy/guardrails/promptguard.mdTests:
Architecture
Files changed
litellm/types/guardrails.pylitellm/types/proxy/guardrails/guardrail_hooks/promptguard.pylitellm/proxy/guardrails/guardrail_hooks/promptguard/promptguard.pylitellm/proxy/guardrails/guardrail_hooks/promptguard/__init__.pytests/test_litellm/proxy/guardrails/guardrail_hooks/test_promptguard.pyui/litellm-dashboard/public/assets/logos/promptguard.svgui/litellm-dashboard/src/components/guardrails/guardrail_garden_data.tsui/litellm-dashboard/src/components/guardrails/guardrail_garden_configs.tsui/litellm-dashboard/src/components/guardrails/guardrail_info_helpers.tsxdocs/my-website/docs/proxy/guardrails/promptguard.mddocs/my-website/sidebars.jsTest plan
poetry run black --checkpasses on all new/modified Python filespoetry run ruff checkpasses on all new/modified Python filespoetry run mypy --ignore-missing-importspasses (0 issues)check-circular-importspassescheck-import-safetypasses (from litellm import *succeeds)pytest tests/test_litellm/proxy/guardrails/guardrail_hooks/test_promptguard.py)npm run buildsucceeds (UI compiles with no errors)npm run testpasses (373 test files, 3626 tests)